#pragma once

#include <iostream>
#include <queue>
#include <mutex>
#include <pthread.h>
#include "lockGuard.hpp"

// #define INI_MTX(mtx) pthread_mutex_init(&mtx, nullptr)
// #define INI_COND(cond) pthread_cond_init()

const int gDefaultCap = 5;

template <class T>
class BlockQueue
{
private:
    bool isQueueEmpty()
    {
        return bq_.size() == 0;
    }
    bool isQueueFull()
    {
        return bq_.size() == capacity_;
    }

public:
    BlockQueue(int capacity = gDefaultCap) 
                : capacity_(capacity)
    {
        pthread_mutex_init(&mtx_, nullptr);
        pthread_cond_init(&Empty_, nullptr);
        pthread_cond_init(&Full_, nullptr);
    }
    void push(const T &in) // 生产者
    {
        // pthread_mutex_lock(&mtx_);
        // //1. 先检测当前的临界资源是否能够满足访问条件
        // // pthread_cond_wait: 我们竟然是在临界区中！我是持有锁的！如果我去等待了，锁该怎么办呢？
        // // pthread_cond_wait第二个参数是一个锁，当成功调用wait之后，传入的锁，会被自动释放！
        // // 当我被唤醒时，我从哪里醒来呢？？从哪里阻塞挂起，就从哪里唤醒, 被唤醒的时候，我们还是在临界区被唤醒的啊
        // // 当我们被唤醒的时候，pthread_cond_wait，会自动帮助我们线程获取锁
        // // pthread_cond_wait: 但是只要是一个函数，就可能调用失败
        // // pthread_cond_wait: 可能存在 伪唤醒 的情况
        // while(isQueueFull()) pthread_cond_wait(&Full_, &mtx_);
        // //2. 访问临界资源，100%确定，资源是就绪的！
        // bq_.push(in);
        // // if(bq_.size() >= capacity_/2) pthread_cond_signal(&Empty_);
        // pthread_cond_signal(&Empty_);
        // pthread_mutex_unlock(&mtx_);
        lockGuard lockgrard(&mtx_); // 自动调用构造函数

        while (isQueueFull())
            pthread_cond_wait(&Full_, &mtx_);
        // 2. 访问临界资源，100%确定，资源是就绪的！
        bq_.push(in);
        pthread_cond_signal(&Empty_);
    } // 自动调用lockgrard 析构函数
    void pop(T *out)
    {
        lockGuard lockguard(&mtx_);
        // pthread_mutex_lock(&mtx_);
        while (isQueueEmpty())
            pthread_cond_wait(&Empty_, &mtx_);
        *out = bq_.front();
        bq_.pop();
        pthread_cond_signal(&Full_);
        // pthread_mutex_unlock(&mtx_);
    }
    ~BlockQueue()
    {
        pthread_mutex_destroy(&mtx_);
        pthread_cond_destroy(&Empty_);
        pthread_cond_destroy(&Full_);
    }

private:
    std::queue<T> bq_;     // 阻塞队列
    int capacity_;         // 容量上限
    pthread_mutex_t mtx_;  // 通过互斥锁保证队列安全
    pthread_cond_t Empty_; // 用它来表示bq 是否空的条件
    pthread_cond_t Full_;  // 用它来表示bq 是否满的条件
};
